#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
STM32Intermediate value模型ConfigurationGenerate器

功能：
1. Generateintermediate_config.hConfigurationFile
2. Generate随机TestKeyAndInputData
3. ConfigurationAlgorithmAndIntermediate value模型选择
"""

import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Tuple

# Addside_channel_attacksPath
sys.path.insert(0, str(Path(__file__).parent.parent / 'side_channel_attacks'))


# ========================================================================
# AlgorithmAnd模型Configuration
# ========================================================================

ALGORITHM_CONFIG = {
    'aes': {
        'name': 'AES-128',
        'key_size': 16,
        'input_size': 16,
        'models': {
            'sbox_output': {
                'name': 'S-box Output',
                'define': 'MODEL_AES_SBOX_OUTPUT',
                'description': 'SBOX[P[0] ⊕ K[0]]'
            },
            'sbox_input': {
                'name': 'S-box Input',
                'define': 'MODEL_AES_SBOX_INPUT',
                'description': 'P[0] ⊕ K[0]'
            },
            'hw_sbox_output': {
                'name': 'HW(S-box Output)',
                'define': 'MODEL_AES_HW_SBOX_OUTPUT',
                'description': 'HW(SBOX[P[0] ⊕ K[0]])'
            },
            'hd_plaintext_sbox': {
                'name': 'HD(Plaintext, S-box)',
                'define': 'MODEL_AES_HD_PLAINTEXT_SBOX',
                'description': 'HD(P[0], SBOX[P[0] ⊕ K[0]])'
            },
            'round_key_xor': {
                'name': 'Round Key XOR',
                'define': 'MODEL_AES_ROUND_KEY_XOR',
                'description': 'AddRoundKey operation'
            },
            'hw_round_key_xor': {
                'name': 'HW(Round Key XOR)',
                'define': 'MODEL_AES_HW_ROUND_KEY_XOR',
                'description': 'HW(P[0] ⊕ K[0])'
            },
        }
    },
    'sm4': {
        'name': 'SM4',
        'key_size': 16,
        'input_size': 16,
        'models': {
            'sbox_output': {
                'name': 'S-box Output',
                'define': 'MODEL_SM4_SBOX_OUTPUT',
                'description': 'SBOX[P[0] ⊕ K[0]]'
            },
            'sbox_input': {
                'name': 'S-box Input',
                'define': 'MODEL_SM4_SBOX_INPUT',
                'description': 'P[0] ⊕ K[0]'
            },
            'hw_sbox_output': {
                'name': 'HW(S-box Output)',
                'define': 'MODEL_SM4_HW_SBOX_OUTPUT',
                'description': 'HW(SBOX[P[0] ⊕ K[0]])'
            },
            'tau_transform': {
                'name': 'Tau Transform',
                'define': 'MODEL_SM4_TAU_TRANSFORM',
                'description': '4 parallel S-boxes'
            },
            'round_function_input': {
                'name': 'Round Function Input',
                'define': 'MODEL_SM4_ROUND_FUNCTION_INPUT',
                'description': 'X1 ⊕ X2 ⊕ X3 ⊕ RK'
            },
        }
    },
    'rsa': {
        'name': 'RSA-2048',
        'key_size': 32,  # 简化版只用32字节
        'input_size': 4,
        'models': {
            'modular_mult': {
                'name': 'Modular Multiplication',
                'define': 'MODEL_RSA_MODULAR_MULT',
                'description': '(input * key) mod N'
            },
            'square_op': {
                'name': 'Square Operation',
                'define': 'MODEL_RSA_SQUARE_OP',
                'description': 'input^2 mod N'
            },
            'hw_result': {
                'name': 'HW(Modular Result)',
                'define': 'MODEL_RSA_HW_RESULT',
                'description': 'HW((input * key) mod N)'
            },
            'modular_add': {
                'name': 'Modular Addition',
                'define': 'MODEL_RSA_MODULAR_ADD',
                'description': '(input + key) mod N'
            },
        }
    },
    'ecc': {
        'name': 'ECC-P256',
        'key_size': 32,
        'input_size': 4,
        'models': {
            'point_add_x': {
                'name': 'Point Addition X',
                'define': 'MODEL_ECC_POINT_ADD_X',
                'description': 'X coordinate of P1 + P2'
            },
            'point_dbl_x': {
                'name': 'Point Doubling X',
                'define': 'MODEL_ECC_POINT_DBL_X',
                'description': 'X coordinate of 2P'
            },
            'scalar_mult_step': {
                'name': 'Scalar Mult Step',
                'define': 'MODEL_ECC_SCALAR_MULT_STEP',
                'description': 'One step in scalar multiplication'
            },
            'hw_coordinate': {
                'name': 'HW(Coordinate)',
                'define': 'MODEL_ECC_HW_COORDINATE',
                'description': 'HW(X coordinate)'
            },
        }
    },
    'sm2': {
        'name': 'SM2',
        'key_size': 32,
        'input_size': 4,
        'models': {
            'point_add_x': {
                'name': 'Point Addition X',
                'define': 'MODEL_SM2_POINT_ADD_X',
                'description': 'X coordinate of P1 + P2'
            },
            'point_dbl_x': {
                'name': 'Point Doubling X',
                'define': 'MODEL_SM2_POINT_DBL_X',
                'description': 'X coordinate of 2P'
            },
            'scalar_mult_step': {
                'name': 'Scalar Mult Step',
                'define': 'MODEL_SM2_SCALAR_MULT_STEP',
                'description': 'One step in scalar multiplication'
            },
            'modular_op': {
                'name': 'Modular Operation',
                'define': 'MODEL_SM2_MODULAR_OP',
                'description': 'Modular arithmetic'
            },
        }
    },
    'dilithium': {
        'name': 'Dilithium-2',
        'key_size': 32,
        'input_size': 8,
        'models': {
            'ntt_butterfly': {
                'name': 'NTT Butterfly',
                'define': 'MODEL_DILITHIUM_NTT_BUTTERFLY',
                'description': 'NTT butterfly operation'
            },
            'poly_mult': {
                'name': 'Polynomial Multiplication',
                'define': 'MODEL_DILITHIUM_POLY_MULT',
                'description': 'Polynomial coefficient multiplication'
            },
            'rejection_sampling': {
                'name': 'Rejection Sampling',
                'define': 'MODEL_DILITHIUM_REJECTION_SAMPLING',
                'description': 'Rejection sampling operation'
            },
            'modular_red': {
                'name': 'Modular Reduction',
                'define': 'MODEL_DILITHIUM_MODULAR_RED',
                'description': 'Modular reduction mod q'
            },
        }
    }
}


def generate_random_data(size: int) -> bytes:
    """Generate随机Data"""
    return os.urandom(size)


def format_byte_array(data: bytes, indent: int = 4) -> str:
    """Format化字节ArrayForC代码"""
    indent_str = ' ' * indent
    lines = []
    for i in range(0, len(data), 16):
        chunk = data[i:i+16]
        hex_str = ', '.join(f'0x{b:02x}' for b in chunk)
        if i + 16 < len(data):
            hex_str += ','
        lines.append(f'{indent_str}{hex_str}')
    return '\n'.join(lines)


def format_input_array(data: bytes, input_size: int, indent: int = 4) -> str:
    """Format化InputArrayForC代码"""
    indent_str = ' ' * indent
    lines = []
    num_inputs = len(data) // input_size
    
    for i in range(num_inputs):
        start = i * input_size
        end = start + input_size
        chunk = data[start:end]
        hex_str = ', '.join(f'0x{b:02x}' for b in chunk)
        if i < num_inputs - 1:
            hex_str += ','
        lines.append(f'{indent_str}{hex_str}')
    
    return '\n'.join(lines)


def generate_config_file(
    algorithm: str,
    model: str,
    num_traces: int = 100,
    trace_delay_us: int = 500000,
    output_dir: Path = None
) -> Tuple[Path, Dict]:
    """
    GenerateConfigurationFile
    
    Args:
        algorithm: Algorithm名称 ('aes', 'sm4', 'rsa', 'ecc', 'sm2', 'dilithium')
        model: 模型名称
        num_traces: Trace数量
        trace_delay_us: Trace延时（微秒）
        output_dir: Output directory
        
    Returns:
        (ConfigurationFilePath, 元DataDictionary)
    """
    if algorithm not in ALGORITHM_CONFIG:
        raise ValueError(f"NotSupport的Algorithm: {algorithm}")
    
    algo_config = ALGORITHM_CONFIG[algorithm]
    
    if model not in algo_config['models']:
        raise ValueError(f"Algorithm{algorithm}NotSupport模型: {model}")
    
    model_config = algo_config['models'][model]
    
    # SettingsOutput directory
    if output_dir is None:
        output_dir = Path(__file__).parent.parent / 'stm32_intermediate_values'
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)
    
    # Generate随机Data
    key_size = algo_config['key_size']
    input_size = algo_config['input_size']
    
    test_key = generate_random_data(key_size)
    test_inputs = generate_random_data(num_traces * input_size)
    
    # Read模板
    template_path = output_dir / 'intermediate_config_template.h'
    if not template_path.exists():
        raise FileNotFoundError(f"模板FileNot存In: {template_path}")
    
    with open(template_path, 'r', encoding='utf-8') as f:
        template = f.read()
    
    # 替换模板变量
    config_content = template.replace('{{GENERATION_TIME}}', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
    config_content = config_content.replace('{{ALGORITHM_NAME}}', algo_config['name'])
    config_content = config_content.replace('{{MODEL_NAME}}', model_config['name'])
    config_content = config_content.replace('{{NUM_TRACES}}', str(num_traces))
    config_content = config_content.replace('{{TRACE_DELAY_US}}', str(trace_delay_us))
    config_content = config_content.replace('{{ALGORITHM_DEFINE}}', f"#define ALGORITHM_{algorithm.upper()}")
    config_content = config_content.replace('{{MODEL_DEFINE}}', f"#define {model_config['define']}")
    config_content = config_content.replace('{{KEY_SIZE}}', str(key_size))
    config_content = config_content.replace('{{INPUT_SIZE}}', str(input_size))
    config_content = config_content.replace('{{TEST_KEY_DATA}}', format_byte_array(test_key))
    config_content = config_content.replace('{{TEST_INPUT_DATA}}', format_input_array(test_inputs, input_size))
    
    # WriteConfigurationFile
    config_path = output_dir / 'intermediate_config.h'
    with open(config_path, 'w', encoding='utf-8') as f:
        f.write(config_content)
    
    # 元Data
    metadata = {
        'algorithm': algorithm,
        'algorithm_name': algo_config['name'],
        'model': model,
        'model_name': model_config['name'],
        'model_description': model_config['description'],
        'num_traces': num_traces,
        'trace_delay_us': trace_delay_us,
        'key_size': key_size,
        'input_size': input_size,
        'test_key': test_key.hex(),
        'config_path': str(config_path)
    }
    
    return config_path, metadata


def list_available_models() -> None:
    """列出All可用的AlgorithmAnd模型"""
    print("\n" + "="*70)
    print("可用的AlgorithmAndIntermediate value模型")
    print("="*70)
    
    for algo, config in ALGORITHM_CONFIG.items():
        print(f"\n【{config['name']}】")
        print(f"  AlgorithmID: {algo}")
        print(f"  Key大小: {config['key_size']} 字节")
        print(f"  Input大小: {config['input_size']} 字节")
        print(f"  可用模型:")
        
        for model_id, model_config in config['models'].items():
            print(f"    • {model_id:<20} - {model_config['name']}")
            print(f"      {model_config['description']}")


def main():
    """主Function"""
    import argparse
    
    parser = argparse.ArgumentParser(description='STM32Intermediate value模型ConfigurationGenerate器')
    parser.add_argument('--algorithm', '-a', type=str, help='Algorithm名称')
    parser.add_argument('--model', '-m', type=str, help='模型名称')
    parser.add_argument('--num-traces', '-n', type=int, default=100, help='Trace数量')
    parser.add_argument('--delay', '-d', type=int, default=500000, help='Trace延时(微秒)')
    parser.add_argument('--output', '-o', type=str, help='Output directory')
    parser.add_argument('--list', '-l', action='store_true', help='列出All可用模型')
    
    args = parser.parse_args()
    
    if args.list:
        list_available_models()
        return
    
    if not args.algorithm or not args.model:
        print("Error: 必须指定AlgorithmAnd模型")
        print("Use --list 查看All可用Options")
        parser.print_help()
        return
    
    try:
        print(f"\nGenerateConfigurationFile...")
        print(f"  Algorithm: {args.algorithm}")
        print(f"  模型: {args.model}")
        print(f"  Trace数: {args.num_traces}")
        print(f"  延时: {args.delay} us")
        
        config_path, metadata = generate_config_file(
            algorithm=args.algorithm,
            model=args.model,
            num_traces=args.num_traces,
            trace_delay_us=args.delay,
            output_dir=args.output
        )
        
        print(f"\n[OK] ConfigurationFileGenerateSuccess!")
        print(f"  Path: {config_path}")
        print(f"\nConfigurationInformation:")
        print(f"  Algorithm: {metadata['algorithm_name']}")
        print(f"  模型: {metadata['model_name']}")
        print(f"  Description: {metadata['model_description']}")
        print(f"  Key大小: {metadata['key_size']} 字节")
        print(f"  Input大小: {metadata['input_size']} 字节")
        print(f"  Trace数量: {metadata['num_traces']}")
        print(f"  TestKey: {metadata['test_key'][:32]}...")
        
        print(f"\n下一步: 编译固件")
        print(f"  cd {Path(config_path).parent}")
        print(f"  make clean && make")
        
    except Exception as e:
        print(f"\n[FAIL] Error: {e}")
        import traceback
        traceback.print_exc()


if __name__ == '__main__':
    main()
